<template>
  <transition v-if="innerValue" name="viewer-fade">
    <div ref="wrapper" :tabindex="-1" class="el-image-viewer__wrapper" :style="{ zIndex }">
      <div class="el-image-viewer__mask" @click.self="hideOnClickModal && hide()"></div>
      <!-- CLOSE -->
      <span class="el-image-viewer__btn el-image-viewer__close" @click="hide">
        <i class="el-icon-close"></i>
      </span>
      <!-- ARROW -->
      <!--
        <template v-if="!isSingle">
        <span
        class="el-image-viewer__btn el-image-viewer__prev"
        :class="{ 'is-disabled': !infinite && isFirst }"
        @click="prev"
        >
        <i class="el-icon-arrow-left"></i>
        </span>
        <span
        class="el-image-viewer__btn el-image-viewer__next"
        :class="{ 'is-disabled': !infinite && isLast }"
        @click="next"
        >
        <i class="el-icon-arrow-right"></i>
        </span>
        </template> 
      -->
      <!-- ACTIONS -->
      <div class="el-image-viewer__btn el-image-viewer__actions">
        <div class="el-image-viewer__actions__inner">
          <i
            v-if="!isSingle"
            class="el-icon-arrow-left"
            :class="{ 'is-disabled': !infinite && isFirst }"
            @click="prev"
          ></i>
          <i
            v-if="!isSingle"
            class="el-icon-arrow-right"
            :class="{ 'is-disabled': !infinite && isLast }"
            @click="next"
          ></i>
          <i class="el-icon-zoom-out" @click="handleActions('zoomOut')"></i>
          <i class="el-icon-zoom-in" @click="handleActions('zoomIn')"></i>
          <i class="el-image-viewer__actions__divider"></i>
          <i :class="mode.icon" @click="toggleMode"></i>
          <i class="el-image-viewer__actions__divider"></i>
          <i class="el-icon-refresh-left" @click="handleActions('anticlocelise')"></i>
          <i class="el-icon-refresh-right" @click="handleActions('clocelise')"></i>
        </div>
      </div>
      <!-- CANVAS -->
      <div class="el-image-viewer__canvas">
        <img
          v-for="(item, i) in urlList"
          v-show="i === index && isImage"
          ref="media"
          :key="`${item.url}image`"
          :src="item.url"
          :style="mediaStyle"
          class="el-image-viewer__img"
          @load="handleMediaLoad"
          @error="handleMediaError"
          @mousedown="handleMouseDown"
        />
        <video
          v-for="(item, i) in urlList"
          v-show="i === index && isVideo"
          ref="media"
          :key="`${item.url}video`"
          controls="controls"
          :src="item.url"
          :style="mediaStyle"
          class="el-image-viewer__img"
          @load="handleMediaLoad"
          @error="handleMediaError"
          @mousedown="handleMouseDown"
        ></video>
        <iframe
          v-for="(item, i) in urlList"
          v-show="i === index && isPdf"
          ref="media"
          :key="`${item.url}pdf`"
          controls="controls"
          :src="item.url"
          :style="mediaStyle"
          class="el-image-viewer__img h-full w-full"
          @load="handleMediaLoad"
          @error="handleMediaError"
          @mousedown="handleMouseDown"
        ></iframe>
      </div>
    </div>
  </transition>
</template>

<script>
const EVENT_CODE = {
  tab: "Tab",
  enter: "Enter",
  space: "Space",
  left: "ArrowLeft", // 37
  up: "ArrowUp", // 38
  right: "ArrowRight", // 39
  down: "ArrowDown", // 40
  esc: "Escape",
  delete: "Delete",
  backspace: "Backspace",
};

const isFirefox = function () {
  return !!window.navigator.userAgent.match(/firefox/i);
};

const rafThrottle = function (fn) {
  let locked = false;
  return function (...args) {
    if (locked) return;
    locked = true;
    window.requestAnimationFrame(() => {
      fn.apply(this, args);
      locked = false;
    });
  };
};

const Mode = {
  CONTAIN: {
    name: "contain",
    icon: "el-icon-full-screen",
  },
  ORIGINAL: {
    name: "original",
    icon: "el-icon-c-scale-to-original",
  },
};

const mousewheelEventName = isFirefox() ? "DOMMouseScroll" : "mousewheel";
const CLOSE_EVENT = "close";
const SWITCH_EVENT = "switch";
import vModelMixin from "@/mixin/v-model";
export default {
  name: "PreviewImgVideo",
  mixins: [vModelMixin],
  props: {
    urlList: {
      type: Array,
      default: () => [], //[{url:'',type:'img'},{url:'',type:'video'},{url:'',type:'pdf'}]
      require: true,
    },
    zIndex: {
      type: Number,
      default: 2000,
    },
    initialIndex: {
      type: Number,
      default: 0,
    },
    infinite: {
      type: Boolean,
      default: true,
    },
    hideOnClickModal: {
      type: Boolean,
      default: false,
    },
  },
  data() {
    return {
      myKeyDownHandler: null,
      myMouseWheelHandler: null,
      myDragHandler: null,
      loading: true,
      index: this.initialIndex,
      media: null,
      mode: Mode.CONTAIN,
      transform: {
        scale: 1,
        deg: 0,
        offsetX: 0,
        offsetY: 0,
        enableTransition: false,
      },
    };
  },
  computed: {
    isSingle() {
      const { urlList } = this;
      return urlList.length <= 1;
    },
    isFirst() {
      return this.index === 0;
    },
    isLast() {
      return this.index === this.urlList.length - 1;
    },
    currentMedia() {
      return this.urlList[this.index];
    },
    isVideo() {
      const currentType = this.urlList[this.index].type;
      return currentType == "video";
    },
    isImage() {
      const currentType = this.urlList[this.index].type;
      return currentType == "img";
    },
    isPdf() {
      const currentType = this.urlList[this.index].type;
      return currentType == "pdf";
    },
    mediaStyle() {
      const { scale, deg, offsetX, offsetY, enableTransition } = this.transform;
      const style = {
        transform: `scale(${scale}) rotate(${deg}deg)`,
        transition: enableTransition ? "transform .3s" : "",
        marginLeft: `${offsetX}px`,
        marginTop: `${offsetY}px`,
      };
      if (this.mode.name === Mode.CONTAIN.name) {
        style.maxWidth = style.maxHeight = "100%";
      }
      return style;
    },
  },
  watch: {
    currentMedia() {
      this.$nextTick(() => {
        const $media = this.media;
        console.log($media);
        if ($media && !$media.complete) {
          this.loading = true;
        }
      });
    },
    index(val) {
      this.reset();
      this.$emit(SWITCH_EVENT, val);
    },
  },
  mounted() {
    this.deviceSupportInstall();
    // add tabindex then wrapper can be focusable via Javascript
    // focus wrapper so arrow key can't cause inner scroll behavior underneath
  },
  destroyed() {
    this.innerValue = false;
  },
  deactivated() {
    this.innerValue = false;
  },
  methods: {
    hide() {
      this.innerValue = false;
      this.deviceSupportUninstall();
      this.$emit(CLOSE_EVENT);
    },

    deviceSupportInstall() {
      this.myKeyDownHandler = rafThrottle((e) => {
        switch (e.code) {
          // ESC
          case EVENT_CODE.esc:
            this.hide();
            break;
          // SPACE
          case EVENT_CODE.space:
            this.toggleMode();
            break;
          // LEFT_ARROW
          case EVENT_CODE.left:
            this.prev();
            break;
          // UP_ARROW
          case EVENT_CODE.up:
            this.handleActions("zoomIn");
            break;
          // RIGHT_ARROW
          case EVENT_CODE.right:
            this.next();
            break;
          // DOWN_ARROW
          case EVENT_CODE.down:
            this.handleActions("zoomOut");
            break;
        }
      });

      this.myMouseWheelHandler = rafThrottle((e) => {
        const delta = e.wheelDelta ? e.wheelDelta : -e.detail;
        if (delta > 0) {
          this.handleActions("zoomIn", {
            zoomRate: 0.015,
            enableTransition: false,
          });
        } else {
          this.handleActions("zoomOut", {
            zoomRate: 0.015,
            enableTransition: false,
          });
        }
      });

      document.addEventListener("keydown", this.myKeyDownHandler, false);
      document.addEventListener(mousewheelEventName, this.myMouseWheelHandler, false);
    },

    deviceSupportUninstall() {
      document.removeEventListener("keydown", this.myKeyDownHandler, false);
      document.removeEventListener(mousewheelEventName, this.myMouseWheelHandler, false);
      this.myKeyDownHandler = null;
      this.myMouseWheelHandler = null;
      this.reset();
    },

    handleMediaLoad() {
      this.loading = false;
    },

    handleMediaError(e) {
      this.loading = false;
    },

    handleMouseDown(e) {
      if (this.loading || e.button !== 0) return;

      const { offsetX, offsetY } = this.transform;
      const startX = e.pageX;
      const startY = e.pageY;
      const wrapper = this.$refs.wrapper;
      const divLeft = wrapper.clientLeft;
      const divRight = wrapper.clientLeft + wrapper.clientWidth;
      const divTop = wrapper.clientTop;
      const divBottom = wrapper.clientTop + wrapper.clientHeight;

      this.myDragHandler = rafThrottle((ev) => {
        this.transform = {
          ...this.transform,
          offsetX: offsetX + ev.pageX - startX,
          offsetY: offsetY + ev.pageY - startY,
        };
      });
      document.addEventListener("mousemove", this.myDragHandler, false);
      document.addEventListener(
        "mouseup",
        (e) => {
          const mouseX = e.pageX;
          const mouseY = e.pageY;
          if (mouseX < divLeft || mouseX > divRight || mouseY < divTop || mouseY > divBottom) {
            this.reset();
          }
          document.removeEventListener("mousemove", this.myDragHandler, false);
        },
        false,
      );

      e.preventDefault();
    },

    reset() {
      this.transform = {
        scale: 1,
        deg: 0,
        offsetX: 0,
        offsetY: 0,
        enableTransition: false,
      };
    },

    toggleMode() {
      if (this.loading) return;

      const modeNames = Object.keys(Mode);
      const modeValues = Object.values(Mode);
      const currentMode = this.mode.name;
      const index = modeValues.findIndex((i) => i.name === currentMode);
      const nextIndex = (index + 1) % modeNames.length;
      this.mode = Mode[modeNames[nextIndex]];
      this.reset();
    },

    prev() {
      if (this.isFirst && !this.infinite) return;
      const len = this.urlList.length;
      this.index = (this.index - 1 + len) % len;
    },

    next() {
      if (this.isLast && !this.infinite) return;
      const len = this.urlList.length;
      this.index = (this.index + 1) % len;
    },

    handleActions(action, options = {}) {
      if (this.loading) return;
      const { zoomRate, rotateDeg, enableTransition } = {
        zoomRate: 0.2,
        rotateDeg: 90,
        enableTransition: true,
        ...options,
      };
      switch (action) {
        case "zoomOut":
          if (this.transform.scale > 0.2) {
            this.transform.scale = parseFloat((this.transform.scale - zoomRate).toFixed(3));
          }
          break;
        case "zoomIn":
          this.transform.scale = parseFloat((this.transform.scale + zoomRate).toFixed(3));
          break;
        case "clocelise":
          this.transform.deg += rotateDeg;
          break;
        case "anticlocelise":
          this.transform.deg -= rotateDeg;
          break;
      }
      this.transform.enableTransition = enableTransition;
    },
  },
};
</script>

<style lang="scss" scoped>
::v-deep .el-image-viewer__actions {
  width: 350px;
}
</style>
